﻿// ----------------------------------------------------------------------------------
// Sample Code from Fernando Machado Píriz's blog: http://fernandomachadopiriz.com
//
// <copyright company="Fernando Machado Píriz" file="SelectPrograms.xaml.cs">
// Copyright (c) Fernando Machado Píriz. Distributed under FSF's GPL v2.</copyright>
// ----------------------------------------------------------------------------------
// This code and information are provided "as is" without warranty of any kind,
// either expressed of implied, including but not limited to the implied warranties
// of merchantability and/or fitness for a particular purpose.
// ----------------------------------------------------------------------------------
namespace ClosePrograms
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Threading;

    /// <summary>
    /// Interaction logic for SelectPrograms.xaml
    /// </summary>
    public partial class SelectPrograms : Window
    {
        /// <summary>
        /// A timer to detect when the user stops typinng in the filter box.
        /// </summary>
        private DispatcherTimer timer = new DispatcherTimer();

        /// <summary>
        /// The list of running programs. 
        /// </summary>
        private IList<RunningProgram> programs;

        /// <summary>
        /// Initializes a new instance of the SelectPrograms class. Initializes a timer to detectect when the user stops typing in the
        /// filter box. Gets a list of running programs. Fills the list box with the list of running programs.
        /// </summary>
        internal SelectPrograms()
        {
            InitializeComponent();

            this.timer.Interval = new TimeSpan(5000000);
            this.timer.Tick += new EventHandler(this.Timer_Tick);

            this.programs = ProgramsEnumerator.GetPrograms();

            this.PopulateList(String.Empty);
        }

        /// <summary>
        /// Creates a <code>SelectPrograms</code> dialog box, populates its fields with received data, and shows it modal; when the user clicks
        /// Ok updates the data back and returns true; returns false otherwise.
        /// </summary>
        /// <param name="owner">The window owning this dialog box.</param>
        /// <param name="data">On entry, the data to populate dialog box fields; on exit, the data entered by the user in dialog box 
        /// fields.</param>
        /// <returns><code>true</code> if the user clicks Ok; <code>false</code> otherwise.</returns>
        internal static bool? ShowDialog(Window owner, SelectProgramsData data)
        {
            SelectPrograms dialog = new SelectPrograms();
            dialog.Owner = owner;
            dialog.UpdateDialogFromData(data);

            bool? result = dialog.ShowDialog();
            if (result.Value == true)
            {
                dialog.UpdataDataFromDialog(data);
            }

            return result;
        }

        /// <summary>
        /// Receives a filter string and a process and returns true if the process's friendly name matches the filter; returns false otherwise.
        /// </summary>
        /// <param name="filter">The filter to apply.</param>
        /// <param name="p">The process to filter.</param>
        /// <returns>Returns true if the process's friendly name matches the filter; false otherwise.</returns>
        private static bool MatchesFilter(string filter, RunningProgram p)
        {
            return String.IsNullOrEmpty(filter) || p.FriendlyName.ToLower().Contains(filter);
        }

        /// <summary>
        /// Fired by the timer when its tick count elapses. Stops the timer so it do not fires again (timer will be started next time the user
        /// starts typing in the filter box, see textBox_TextChanged) and filters the programs shown in the list box.
        /// </summary>
        /// <param name="sender">The parameter is not used.</param>
        /// <param name="e">The parameter is not used.</param>
        private void Timer_Tick(object sender, EventArgs e)
        {
            this.timer.Stop();
            this.PopulateList(textBoxFilter.Text.ToLower());
        }

        /// <summary>
        /// Fired when the use types in the filter box. Starts the timer to filter the list box of programs asynchronously: if the user keeps
        /// typing the timer keeps firing and never elapses; when the user stop typing the timer elapses and the list box is updated (see
        /// Timer_Tick).
        /// </summary>
        /// <param name="sender">The parameter is not used.</param>
        /// <param name="e">The parameter is not used.</param>
        private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
        {
            this.timer.Start();
            this.UpdateSelectedCount();
        }

        /// <summary>
        /// Filters the programs shown in the list box.
        /// </summary>
        /// <param name="filter">The filter to apply.</param>
        private void PopulateList(string filter)
        {
            var list = from p in this.programs
                    where MatchesFilter(filter, p)
                    select p;
            listBox.ItemsSource = list;
            this.UpdateSelectedCount();
        }

        /// <summary>
        /// Updates the label showing the total number of items, selected count, etc.
        /// </summary>
        private void UpdateSelectedCount()
        {
            int selected = 0;
            int unselected = 0;
            foreach (RunningProgram process in this.programs)
            {
                if (process.Selected)
                {
                    selected++;
                }
                else
                {
                    unselected++;
                }
            }

            labelSelected.Content = String.Format(
                "{0} items in total, {1} selected, {2} unselected, showing {3}",
                this.programs.Count,
                selected,
                unselected,
                listBox.Items.Count);
        }

        /// <summary>
        /// Fired when the user checks the check box to select all items in the list box of programs. Selects all items in the list of programs
        /// and updates the list box.
        /// </summary>
        /// <param name="sender">The parameter is not used.</param>
        /// <param name="e">The parameter is not used.</param>
        private void CheckBoxSelectAll_Checked(object sender, RoutedEventArgs e)
        {
            foreach (RunningProgram process in this.programs)
            {
                process.Selected = true;
            }

            this.PopulateList(textBoxFilter.Text);
        }

        /// <summary>
        /// Fired when the uses unchecks the check box to deselect all items in the list box of programs is checked. Selects all items in the
        /// list of programs and updates the list box.
        /// </summary>
        /// <param name="sender">The parameter is not used.</param>
        /// <param name="e">The parameter is not used.</param>
        private void CheckBoxSelectAll_Unchecked(object sender, RoutedEventArgs e)
        {
            foreach (RunningProgram process in this.programs)
            {
                process.Selected = false;
            }

            this.PopulateList(textBoxFilter.Text);
        }

        /// <summary>
        /// Fired when the user checks an item in the list box of programs. Updates the state in the check box to select o deselect all items
        /// in the list box and updates the label with the selected items count.
        /// </summary>
        /// <param name="sender">The parameter is not used.</param>
        /// <param name="e">The parameter is not used.</param>
        private void CheckBoxListBox_Checked(object sender, RoutedEventArgs e)
        {
            checkBoxSelectAll.IsChecked = null;
            this.UpdateSelectedCount();
        }

        /// <summary>
        /// Fired when the user unchecks an item in the list box of programs. Updates the state in the check box to select o deselect all items
        /// in the list box and updates the label with the selected items count.
        /// </summary>
        /// <param name="sender">The parameter is not used.</param>
        /// <param name="e">The parameter is not used.</param>
        private void CheckBoxListBox_Unchecked(object sender, RoutedEventArgs e)
        {
            checkBoxSelectAll.IsChecked = null;
            this.UpdateSelectedCount();
        }

        /// <summary>
        /// Fired when user clicks the Ok button. Closes this dialog box returning true.
        /// </summary>
        /// <param name="sender">The parameter is not used.</param>
        /// <param name="e">The parameter is not used.</param>
        private void ButtonOk_Click(object sender, RoutedEventArgs e)
        {
            this.DialogResult = true;
        }

        /// <summary>
        /// Fired when user clicks the Cancel button. Closes this dialog box returning false.
        /// </summary>
        /// <param name="sender">The parameter is not used.</param>
        /// <param name="e">The parameter is not used.</param>
        private void ButtonCancel_Click(object sender, RoutedEventArgs e)
        {
            this.DialogResult = false;
        }

        /// <summary>
        /// Updates given data record with the values entered by the user in dialog box fields.
        /// </summary>
        /// <param name="data">The data record containing dialog box fields values.</param>
        private void UpdataDataFromDialog(SelectProgramsData data)
        {
            data.ProgramsToClose.Clear();
            foreach (RunningProgram programInfo in this.programs)
            {
                if (programInfo.Selected)
                {
                    data.ProgramsToClose.Add(new ProgramToClose(programInfo.ModuleFileName));
                }
            }
        }

        /// <summary>
        /// Populate dialog box fields with given data record. 
        /// </summary>
        /// <param name="data">The data record containing dialog box fields values.</param>
        private void UpdateDialogFromData(SelectProgramsData data)
        {
            foreach (RunningProgram programInfo in this.programs)
            {
                if (data.ProgramsToClose.FirstOrDefault(item => item.ModuleFileName.Equals(programInfo.ModuleFileName)) != null)
                {
                    programInfo.Selected = true;
                }
            }
        }
    }
}
